package gbench.common.matlib.data;

import java.util.function.Function;

/**
 * 计算管道:节段.
 * 管道的定义初衷就是解决一个 定义函数太过负载的问题：<br>
 * final Function<Integer,Integer> plusTwo = (a)->a+2 <br>
 * 可以被简化为：<br>
 * final var plusTwo = DUMMY(Integer.class).bind(e->e+2); <br>
 * 以后需要调用的plusTwo的时候，传入plusTwo::evaluate就可以代替了。 <br>
 * 
 * @author Administrator
 *
 * @param <T> 输入类型
 * @param <U> 输出类型
 */
public class Pipe<T, U> {

    /**
     * 构造一个计算管道
     * 
     * @param evaluator 输入数据
     * @param evaluator 数据运算
     */
    public Pipe(final T data, final Function<T, U> evaluator) {
        this.data = data;
        this.evaluator = evaluator;
    }

    /**
     * 把一个函数绑定到计算管道中去
     * 
     * @param <V>       管道输出类型
     * @param evaluator 绑定的函数运算
     * @return [t->u->v] 的计算管道
     */
    @SuppressWarnings("unchecked")
    public <V> Pipe<T, V> bind(final Function<U, V> evaluator) {
        if (this.evaluator == null) {
            this.evaluator = (Function<T, U>) evaluator;
            return (Pipe<T, V>) this;
        } else {
            return PIPE(this.data, evaluator.compose(this.evaluator));
        } // if
    }
    
    /**
     * 把一个T->U类型的管道强制转换成T->V类型的管道，<br>
     * 也就是说是的可以bind 到v->?类型的函数 <br>
     * 相当于 : bind(CAST(clazz));
     * 
     * @param <V>       管道输出类型
     * @param clazz     把输出类型强制转换成clazz类型
     * @return [t->v] 的计算管道
     */
    public <V> Pipe<T, V> cast(final Class<V> clazz) {
        return bind(CAST(clazz));
    }
    
    /**
     * 把一个T->U类型的管道强制转换成T->V类型的管道，<br>
     * 也就是说是的可以bind 到v->?类型的函数 <br>
     * 相当于 : bind(CAST(clazz));
     * 
     * @param <V>       管道输出类型
     * @param dummy 输入数据 占位符,用于提示编译器编译正确类型
     * @return [t->v] 的计算管道
     */
    public <V> Pipe<T, V> cast(final V dummy) {
        return bind(CAST(dummy));
    }

    /**
     * 采用默认值的管道计算<br>
     * 当data 不为null && evaluator 为空的时候 返回 data <br>
     * 否则当 evaluator 为null 返回null <br>
     * 否则  evaluator.apply(data); // 注意此种情况包含(evaluator!=null && data==null) 的方式。<br>
     * 有效性请用 调用者给给予保证<br>
     * 
     * @return U 类型的结果
     */
    @SuppressWarnings("unchecked")
    public U evaluate() {
        if (this.data != null && evaluator == null) return (U) this.data;
        else if (evaluator == null) return null;
        else return evaluator.apply(data);
    }

    /**
     * 使用指定值得管道计算
     * 当evaluator为null时候，直接放回data 
     * @param data 计算的参数数据
     * @return U 类型的结果
     */
    @SuppressWarnings("unchecked")
    public U evaluate(final T data) {
        if(evaluator==null)return (U)data;
        return evaluator.apply(data);
    }
    
    /**
     * 使用指定值得管道计算（evaluate的别名）
     * 
     * @param data 计算的参数数据
     * @return U 类型的结果
     */
    public U eval(final T data) {
        return evaluate(data);
    }
    
    /**
     * 函数化：t->u
     * @return 返回函数接口
     */
    public Function<T,U> functionalize(){
        return this::evaluate;
    };

    /**
     * 构造一个计算管道
     * 
     * @param <T> 输入参数类型
     * @param <U> 输出数据类型
     * @param data      输入数据
     * @param evaluator 数据运算
     * @return [t->u]的计算管道
     */
    public static <T, U> Pipe<T, U> PIPE(final T data, final Function<T, U> evaluator) {
        return new Pipe<>(data, evaluator);
    }

    /**
     * 构造一个计算管道
     * 
     * @param <T> 输入参数类型
     * @param <U> 输出数据类型
     * @param evaluator 数据运算
     * @return [t->u]的计算管道
     */
    public static <T, U> Pipe<T, U> PIPE(final Function<T, U> evaluator) {
        return new Pipe<>(null, evaluator);
    }

    /**
     * 构造一个计算管道
     * 
     * @param <T> 输入参数类型
     * @param data 输入数据
     * @return [t->t]的计算管道
     */
    public static <T> Pipe<T, T> PIPE(final T data) {
        return new Pipe<T, T>(data, null);
    }
    
    /**
     * 构造一个计算管道:不带有数值参数，只是提供类型占位。<br>
     * 等价于 new Pipe &lt; T, T &gt; ((T)null, null);<br>
     * 所谓DUMMY就是说不能被调用（直接evaluate())，<br>
     * 只能被征用 提供数据data让 dummy 来计算 dummy.evaluate(data)的管道。 <br>
     * 
     * @param <T> 输入数据的类型
     * @param clazz 输入数据 的类型类
     * @return [t->t]的计算管道
     */
    public static <T> Pipe<T, T> DUMMY(final Class<T> clazz) {
        return new Pipe<T, T>((T)null, null);
    }
    
    /**
     * 构造一个计算管道:不带有数值参数，只是提供类型占位。<br>
     * 等价于 new Pipe &lt; T, T &gt; ((T)null, null);<br>
     * 所谓DUMMY就是说不能被调用（直接evaluate())，<br>
     * 只能被征用 提供数据data让 dummy 来计算 dummy.evaluate(data)的管道。 <br>
     * 
     * @param <T> 输入数据的类型
     * @param dummy 输入数据 的类型类,占位符的类型
     * @return [t->t]的计算管道
     */
    public static <T> Pipe<T, T> DUMMY(T dummy) {
        return new Pipe<T, T>((T)null, null);
    }
    
    /**
     * 编译辅助函数<br>
     * 强制类型转换 函数:就是 t->(U)t的简写<br>
     * 设计动机就是因为：管道可以被视作一个类型化的 lambda 函数<br>
     * 例如：<br>
     * final var lambda = PIPE(NULL(Object[].class)).bind(CAST(Double[][].class)).bind(XVec::OF).bind(v->v.fmap(NVec::new));// 使用管道定义函数 lambda<br>
     * final var xvec = rG(1, 100).mutate(NVec::new).reshape(lambda::evaluate,10,10); // 使用管道函数 <br>
     * System.out.println(xvec);<br>
     * @param <T> 输入参数类型
     * @param <U> 输出结果类型
     * @param clazz 输出结构类型：类型占位符
     * @return 一个把输入T类型强制转换成 输出U类型的中间函数
     */
    @SuppressWarnings("unchecked")
    public static <T,U> Function<T,U> CAST(final Class<U> clazz){
        return t->(U)t;
    }
    
    /**
     * 编译辅助函数<br>
     * 强制类型转换 函数:就是 t->(U)t的简写<br>
     * 设计动机就是因为：管道可以被视作一个类型化的 lambda 函数<br>
     * 例如：<br>
     * final var lambda = PIPE(NULL(Object[].class)).bind(CAST(Double[][].class)).bind(XVec::OF).bind(v->v.fmap(NVec::new));// 使用管道定义函数 lambda<br>
     * final var xvec = rG(1, 100).mutate(NVec::new).reshape(lambda::evaluate,10,10); // 使用管道函数 <br>
     * System.out.println(xvec);<br>
     * @param <T> 输入参数类型
     * @param <U> 输出结果类型
     * @param u 类型占位符
     * @return 一个把输入T类型强制转换成 输出U类型的中间函数
     */
    @SuppressWarnings("unchecked")
    public static <T,U> Function<T,U> CAST(final U u){
        return t->(U)t;
    }
    
    /**
     * 编译辅助函数<br>
     * 返回一个 U类型的空值 null，就是(U)null的简写 <br>
     * 设计动机就是因为：管道可以被视作一个类型化的 lambda 函数<br>
     * 例如：<br>
     * final var lambda = PIPE(NULL(Object[].class)).bind(CAST(Double[][].class)).bind(XVec::OF).bind(v->v.fmap(NVec::new));// 使用管道定义函数lambda <br>
     * final var xvec = rG(1, 100).mutate(NVec::new).reshape(lambda::evaluate,10,10); // 使用管道函数 <br>
     * System.out.println(xvec);<br>
     * 就定义了 一个 接收Object[] 类型的管道函数。
     * @param <U> U类型
     * @param clazz 编译提示的类型占位符
     * @return U 类型的空值
     */
    public static  <U> U NULL(final Class<U> clazz){
        return (U)null;
    }

    private T data;// 管道数据
    private Function<T, U> evaluator; // 管道计算器

}